#include "environment.h"
#include <string.h>

Environment::~Environment()
{
  deleteEnvironment(m_root, 0);
}

Lexeme* Environment::cons(Lexeme* a, Lexeme* b)
{
  Lexeme* n = new Lexeme();
  n->leftPtr = a;
  n->rightPtr = b;
  return n;
}

Lexeme* Environment::car(Lexeme* env)
{
  return env->leftPtr;
}

Lexeme* Environment::cdr(Lexeme* env)
{
  return env->rightPtr;
}

Lexeme* Environment::cadr(Lexeme* env)
{
  return car(cdr(env));
}

Lexeme* Environment::caddr(Lexeme* env)
{
  return car(cdr(cdr(env)));
}

Lexeme* Environment::cadddr(Lexeme* env)
{
  return car(cdr(cdr(cdr(env))));
}

Lexeme* Environment::newEnvironment()
{
  m_root = cons(cons(NULL, cons(NULL, NULL)), NULL);
  return m_root;
}

Lexeme* Environment::populate(Lexeme* var, Lexeme* val, Lexeme* env)
{
  Lexeme* local = car(env);
  local->leftPtr = cons(var, local->leftPtr);
  local->rightPtr->leftPtr = cons(val, local->rightPtr->leftPtr);
  return val;
}

bool Environment::matchVar(Lexeme* a, Lexeme* b)
{
  if (strcmp(a->getTVal(), b->getTVal()) == 0)
  {
    if(strcmp(a->getSVal().c_str(), b->getSVal().c_str()) == 0)
      return true;
    else
      return false;
  }
  else
    return false;
}

Lexeme* Environment::update(Lexeme* var, Lexeme* val, Lexeme* env)
{
  while (env != NULL)
  {
    Lexeme* local = car(env);
    Lexeme* vars = car(local);
    Lexeme* vals = cadr(local);
    while(vars != NULL)
    {
      if(matchVar(var, car(vars)))
      {
        populate(car(vars), val, env);
        return vals;
      }
      else
      {
        vars = cdr(vars);
        vals = cdr(vals);
      }
    };
    env = cdr(env);
  };
  return NULL; //throw here

}

Lexeme* Environment::lookup(Lexeme* var, Lexeme* env)
{
  while (env != NULL)
  {
    Lexeme* local = car(env);
    Lexeme* vars = car(local);
    Lexeme* vals = cadr(local);
    while (vars != NULL)
    {
      if (matchVar(var, car(vars)))
        return car(vals);
      else
      {
        vars = cdr(vars);
        vals = cdr(vals);
      }
    };
    env = cdr(env);
  };
  return NULL; //throw here
}

Lexeme* Environment::extend(Lexeme* vars, Lexeme* vals, Lexeme* env)
{
  return cons(cons(vars, cons(vals, NULL)), env);
}

void Environment::print(Lexeme* root, int level)
{
  if (root != NULL)
  {
    if(level != 0)
      if(strcmp(root->sVal.c_str(), "ROOT") == 0)
        return;
    printf("%d - %s %d %s\n",level, root->tVal, root->iVal, root->sVal.c_str());
    level++;
    print(car(root),level);
    print(cdr(root),level);
  }
}

void Environment::deleteEnvironment(Lexeme* root, int level)
{
  if ( (root != NULL) && (level != 0) && (strcmp(root->sVal.c_str(), "ROOT") == 0))
  {
    level++;
    deleteEnvironment(car(root), level);
    deleteEnvironment(cdr(root), level);
    delete root;
    root = NULL;
  }
}
